package personal.util;

import java.util.*;

/**
 * 用于自动分配子集 id
 *
 * @author 陈渤林
 */
public class IdAllocter<T> {
    /**
     * 从子集到 id 的映射信息
     */
    private Map<T, Integer> objectToId;
    /**
     * 从 id 到子集的映射信息
     */
    private Map<Integer, T> idToObject;

    public Map<Integer, T> getIdToObject() {
        return idToObject;
    }

    /**
     * 辅助队列：当一个新的 id 被分配，其子集将会放进此处
     */
    private Queue<T> newObjectBuffer;

    /**
     * 将辅助队列里的所有内容放入指定容器中
     *
     * @param x 指定容器
     * @return 放入了几个元素
     */
    public int pollAllTo(final Collection<T> x) {
        int cnt = 0;
        while (!newObjectBuffer.isEmpty()) {
            x.add(newObjectBuffer.poll());
            ++cnt;
        }
        return cnt;
    }

    /**
     * 初始 id，从这个值开始分配
     */
    private int startId;

    public int getStartId() {
        return startId;
    }

    public void setStartId(int startId) {
        this.startId = startId;
    }

    /**
     * 构造函数：初始化空集的 id 为 -1，绑定辅助队列
     */
    public IdAllocter(int startId) {
        this.objectToId = new HashMap<T, Integer>();
        this.idToObject = new HashMap<Integer, T>();
        this.newObjectBuffer = new LinkedList<T>();
        this.startId = startId;
    }

    /**
     * 给 object 分配一个 id，如果之前未分配，则将其放入辅助队列，
     *
     * @param object
     * @return object 的 id
     */
    public int getId(final T object) {
        int id = this.startId + this.objectToId.size();
        if (null == this.objectToId.putIfAbsent(object, id)) { // 新 id
            this.idToObject.put(id, object);
            this.newObjectBuffer.add(object);
            return id;
        }
        return this.objectToId.get(object);
    }

    /**
     * 查询指定 id 的 object
     *
     * @param id 指定 id
     * @return 对应的 object
     */
    public T getObject(int id) {
        return this.idToObject.get(id);
    }

    /**
     * @return 对象数量
     */
    public int size() {
        return this.objectToId.size();
    }


    /**
     * 将子集 id 构造表转化成字符串
     */
    @Override
    public String toString() {
        String ret = "";
        for (Map.Entry<Integer, T> kv : idToObject.entrySet())
            ret += String.format("%d: %s\n", kv.getKey(), kv.getValue().toString());
        return ret;
    }
}
